Provider-Agnostic Payment Gateway Design
I would define an application-level payment abstraction and implement one adapter per provider. The business layer should work with our own PaymentRequest and PaymentResult models rather than provider-specific SDK types. A factory or dependency-injection strategy can select the appropriate provider. This isolates vendor-specific APIs and makes adding another provider much safer.
Use an abstraction owned by the application rather than exposing provider SDK types.
Use Adapter implementations to translate provider-specific APIs.
Select providers through dependency injection, configuration, routing policy, or a factory.
Normalize provider errors into meaningful application-level error categories.
Design for idempotency because payment requests may be retried.
Handle webhooks, asynchronous payment states, timeouts, retries, reconciliation, and duplicate notifications.
Never assume a successful API response alone means the entire payment lifecycle is complete.